﻿//////////////////////////////////////////////////////////////////////
//                       Class OCRnew                               //
//               OCR klasse die tekens kan leren,                   //
//               in een teksbestand zet, en aan de hand             //
//               daarvan de tekens kan herkennen                    //
//                                                                  //
//                          Made By:                                //
//                      Guido van Hienen                            //
//                      Hessel Meulenbeld                           //
//                                                                  //
//                          Version:                                //
//                1.0 - Guido van Hienen - ??-??-????               //
//                1.1 - Hessel Meulenbeld - 9-03-2012               //
//                      - Re-made first code to new OCR             //
//                        based on line-detection                   //
//                1.2 - Hessel Meulenbeld - 16-03-2012              //
//                      - Made the class compatible with            //
//                        the AlgorithmBase code                    //
//                1.3 - Hessel Meulenbeld - 16-03-2012              //
//                      - Used a simple method(Tjerk Slaghek)       // 
//                        for sorting the char array.               //
//                      - Added comments                            //
//                      - Made code more readable                   //
//                                                                  //
//////////////////////////////////////////////////////////////////////
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using THO7AlgorithmBase;

namespace OCR_LEARN {
    class OCR : OCRAlgorithm{
        public OCR(String name) : base(name) { }
        public void OCR_learn(Bitmap sourceImage,String l) {
            Bitmap temp = new Bitmap(sourceImage);
            //definïeer 2 files waar de controle-waardes worden opgeslagen.
            String links_file = "OCRlinks.txt";
            String boven_file = "OCRboven.txt";
            //kijk of files bestaan, zoniet maak ze aan
            if (!File.Exists(links_file)) {
                StreamWriter sw = new StreamWriter(links_file, true);
                sw.Close();
            }
            if (!File.Exists(boven_file)) {
                StreamWriter sw = new StreamWriter(boven_file, true);
                sw.Close();
            }
            //ga het teken leren vanaf links.
            learn(temp, l,links_file, "links");
            //ga het teken leren vanaf boven.
            temp.RotateFlip(RotateFlipType.Rotate90FlipNone);
            learn(temp, l,boven_file, "boven");
        }

        public override double[] DoOCR(Bitmap sourceImage) {
            int aantal = 36;
            //String[] volgorde = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
            
            //maak 2 letter arrays die gevuld worden met waardes van links en van boven.
            letter[] links = new letter[aantal];
            letter[] boven = new letter[aantal];
            //letter[] l = new letter[aantal];
            double[] new_l = new double[aantal];
            Bitmap temp = new Bitmap(sourceImage);
            //bereken de overeenkomst-percentages vanaf links.
            links = recognize(temp, "OCRlinks.txt", "links", aantal);
            //bereken de overeenkomst-percentages vanaf rechts.
            temp.RotateFlip(RotateFlipType.Rotate90FlipNone);
            boven = recognize(temp, "OCRboven.txt", "boven", aantal);
            // voeg percentages samen, en sorteer ze op tekens.
            for (int i = 0; i < links.Length; i++) {
                for (int j = 0; j < boven.Length; j++) {
                    if (links[i].Teken.Equals(boven[j].Teken)) {
                        new_l[characterToIndex(links[i].Teken)] = (int)((links[i].Waarde + boven[j].Waarde) / 2.52);
                        //l[i] = new letter(links[i].get_letter(), (int)((links[i].get_waarde()+boven[j].get_waarde())/2.52));
                        break;
                    }
                }
            }
            /*for(int i = 0; i < l.Length; i++){
                for(int j = 0; j < volgorde.Length; j++){
                    if(l[i].get_letter().Equals(volgorde[j])){
                        new_l[j] = l[i].get_waarde();
                        break;
                    }
                }
            }*/
            return new_l;
        }

        public letter[] recognize(Bitmap sourceImage, String file,String dir,int return_aantal) {
            //lees het bestand uit, en sla de gelezen characters op.
            StreamReader reader = new StreamReader(file);
            List<String> characters = new List<String>();
            String s = "";
            while ((s = reader.ReadLine()) != null) {
                characters.Add(s);
            }
            reader.Close();

            //lock de bitmap data en doorloop het plaatje naar het aantal overgangen naar zwart.
            Bitmap returnImage = new Bitmap(sourceImage);
            returnImage = prepare_image(returnImage, dir);
            int[] zwart = zoek_overgangen(returnImage);

            //maak arrays aan voor de percentage waardes.
            int[] hoogsten = new int[return_aantal];
            letter[] hoogste = new letter[return_aantal];
            for (int q = 0; q < hoogste.Length; q++) {
                hoogsten[q] = 0;
                hoogste[q] = new letter("?", 0);
            }
            for (int i = 0; i < characters.Count; i++) {
                //haal eerst de waardes uit de string van het tekst document
                string[] words = characters[i].Split(' ');
                int[] f = new int[words.Length];
                for (int j = 1; j < words.Length; j++) {
                    f[j-1] = int.Parse(words[j]);
                }
                //kijk hoeveel van de waardes overeenkomen.
                int goed = 0;
                for (int j = 0; j < returnImage.Height; j++) {
                    int temp = zwart[j] - f[j];
                    if (temp < 1000 && temp > -1000) {
                        goed += 1;
                    }
                }
                //sorteer de array om de laagste waarde onderaan te plaatsen
                Array.Sort(hoogsten);
                //plaats de hoogste waardes in de letter[] hoogste.
                if (goed > hoogsten[0]) {
                    for (int l = 0; l < hoogste.Length; l++) {
                        if (hoogste[l].Waarde == hoogsten[0]) {
                            hoogste[l] = new letter(words[0], goed);
                            break;
                        }
                    }
                    hoogsten[0] = goed;
                }
            }
            return hoogste;
        }

        public void learn(Bitmap sourceImage, String character,String file,String dir) {
            //lees de meegeleverde file uit en slag de character op
            StreamReader reader = new StreamReader(file);
            List<String> characters = new List<String>();
            String s = "";
            while ((s = reader.ReadLine()) != null) {
                characters.Add(s);
            }
            reader.Close();

            //bereid de bitmap voor
            Bitmap returnImage = new Bitmap(sourceImage);
            returnImage = prepare_image(returnImage, dir);
            //zoek overgangen
            int[] zwart = zoek_overgangen(returnImage);

            //kijk of het teken al eerder is geleerd
            Boolean gevonden = false;
            for (int i = 0; i < characters.Count; i++) {
                if (characters[i][0] == character[0]) {
                    //combineer de verschillende gemeten waardes
                    //haal eerst de waardes uit de string van het tekst document
                    string[] words = characters[i].Split(' ');
                    int[] f = new int[words.Length];
                    for (int j = 1; j < words.Length; j++) {
                        f[j-1] = int.Parse(words[j]);
                    }
                    String temp = character;
                    for (int j = 0; j < f.Length- 2; j++) {
                        f[j] = ((f[j]*f[f.Length-2] + zwart[j]) / (f[f.Length-2]+1));
                        temp += " " + f[j];
                    }
                    f[f.Length-2]++;
                    temp += " " + f[f.Length-2];
                    characters[i] = temp;
                    gevonden = true;
                    break;
                }
            }
            //als hij nog niet is gevonden, plaats de nieuwe waardes in de file.
            if (!gevonden) {
                String waardes = character;
                for (int j = 0; j < (returnImage.Height); j++) {
                    waardes += " " + zwart[j];
                }
                waardes += " " + 1;
                characters.Add(waardes);
            }
            //schrijf nu alle waardes terug naar de tekst file
            StreamWriter writer = new StreamWriter(file);
            for (int i = 0; i < characters.Count; i++) {
                writer.WriteLine(characters[i]);
            }
            writer.Close();
        }
        
        public int[] zoek_overgangen(Bitmap source) {
            //bitmap data locken
            Rectangle rect = new Rectangle(0, 0, source.Width, source.Height);
            BitmapData myBmpData = source.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            IntPtr ptr = myBmpData.Scan0;
            int numBytes = source.Width * source.Height * 3;
            byte[] rgbValues = new byte[numBytes];
            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, numBytes);

            int[] zwart = new int[source.Height];
            for (int i = 0; i < source.Height; i++) {
                bool start = false;
                for (int j = 0; j < source.Width * 3; j += 3) {
                    if (!start) {
                        // als de kleur zwart gedetecteerd wordt(en de start boolean is vrijgegeven), wordt er bij de zwarte
                        // waarde voor die rij 1000(voor de nauwkeurigheid) bijgeteld.
                        if (rgbValues[(i * source.Width * 3) + j] == 0) {
                            start = true;
                            zwart[i] += 1000;
                        }
                    } else {
                        // bij een witte kleur word de start boolean weer vrijgegeven.
                        if (rgbValues[(i * source.Width * 3) + j] == 255) {
                            start = false;
                        }
                    }
                }
            }
            //bitmap data unlocken
            System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, numBytes);
            source.UnlockBits(myBmpData);
            return zwart;
        }

        public Bitmap prepare_image(Bitmap prepare,String dir) {
            ///
            /// bewerk de bitmap om de juiste input te krijgen.
            /// 
            VisionAlgorithm fp = new remove_borders("");
            prepare = fp.DoAlgorithm(prepare);
            if (dir == "links") {
                VisionAlgorithm stretch = new stretch("", 150, 100);
                prepare = stretch.DoAlgorithm(prepare);
            } else {
                VisionAlgorithm stretch = new stretch("", 100, 150);
                prepare = stretch.DoAlgorithm(prepare);
            }
            return prepare;
        }
        
        private int characterToIndex(String s) {
            //haal de juiste index bij een bepaald character
            int arrayIndex = 0;
            int asciiValue = Encoding.ASCII.GetBytes(s)[0];
            if ((asciiValue < 58) && (asciiValue > 47)) //0-9
            {
                arrayIndex = asciiValue - 48;
            }
            if ((asciiValue < 91) && (asciiValue > 64)) //A-Z
            {
                arrayIndex = (asciiValue - 65) + 10;
            }
            return arrayIndex;
        }
    }
    
    public class letter {
        //deze object klasse slaat een string en bijbehorende waarde op
        String letter1 = "";
        int waarde = 0;
        public letter(String l, int w) {
            letter1 = l;
            waarde = w;
        }
        public int Waarde{
            get {return waarde;}
            set { waarde = value; }
        }
        public String Teken{
            get {return letter1;}
            set {letter1 = value;}
        }
    }
}
